/*
 * Copyright 2008-2009 ArtisanLogic.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.artisanlogic.jingo.assembler;

import static com.artisanlogic.lang.Utils.requireParameter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.SortedSet;

import org.apache.commons.codec.binary.Hex;

import com.artisanlogic.io.ClosingStreamTemplate;
import com.artisanlogic.io.StreamTask;
import com.artisanlogic.jingo.parser.Handler;
import com.artisanlogic.jingo.parser.Parser;

// =====================================================================================================================
// FileHandler.java                                                                              Created on Feb 18, 2009
// ---------------------------------------------------------------------------------------------------------------------
/**
 * This Handler implementation writes Jingo dependency graph assemblies to the appropriate destination file.
 * 
 * @author sean@artisanlogic.com
 * @author john@artisanlogic.com
 * @since 0.9-Beta
 */
// =====================================================================================================================
public class FileHandler implements Handler {

  // -------------------------------------------------------------------------------------------------------------------
  // private instance methods
  // -------------------------------------------------------------------------------------------------------------------

  private final UrlMappingStrategy urlMapper;

  private final Parser parser;

  private final File outputDir;

  private ClosingStreamTemplate<OutputStream> outputStreamTemplate = new ClosingStreamTemplate<OutputStream>();

  // -------------------------------------------------------------------------------------------------------------------
  // constructors
  // -------------------------------------------------------------------------------------------------------------------

  /**
   * This constructor creates a new FileHandler with the specified values for the urlMapper and outputDir properties.
   * 
   * @param urlMapper This parameter holds the URL mapping strategy to be used to map repository prefixes to actual
   *          universal resource locators.
   * @param outputDir This parameter holds the File representation of the directory that Jingo module dependency graph
   *          assemblies should be written to.
   */
  public FileHandler(UrlMappingStrategy urlMapper, Parser parser, File outputDir) {
    requireParameter("urlMapper", urlMapper);
    this.urlMapper = urlMapper;
    requireParameter("parser", parser);
    this.parser = parser;
    requireParameter("outputDir", outputDir);
    this.outputDir = outputDir;
  }

  // -------------------------------------------------------------------------------------------------------------------
  // public instance methods
  // -------------------------------------------------------------------------------------------------------------------

  /*
   * (non-Javadoc)
   * 
   * @see com.artisanlogic.jingo.parser.Handler#handleDependencies(int, java.util.SortedSet)
   */
  public void handleDependencies(final int depth, final SortedSet<Dependency> dependencies) {
    outputStreamTemplate.withStream(new StreamTask<OutputStream>() {
      public OutputStream getStream() throws Exception {
        return createOutputStream(getFile(dependencies));
      }
      public void execute(OutputStream output) throws Exception {
        createAssember(output).handleDependencies(1, dependencies);
      }
    });
  }

  // -------------------------------------------------------------------------------------------------------------------
  // protected instance methods
  // -------------------------------------------------------------------------------------------------------------------

  /**
   * This method abstracts the {@link Assembler} creation process
   * 
   * @param output {@link OutputStream} for the Assembler to write to
   * @return This method returns a newly constructed {@link Assembler} instance.
   */
  protected Assembler createAssember(OutputStream output) {
    return new Assembler(urlMapper, parser, output, FileHandler.this);
  }

  /**
   * This method abstracts the {@link FileOutputStream} creation process
   * 
   * @param file The {@link File} to create a {@link FileOutputStream} from
   * @return This method returns a {@link FileOutputStream} for the {@link File} passed in.
   * @throws FileNotFoundException
   */
  protected FileOutputStream createOutputStream(File file) throws FileNotFoundException {
    return new FileOutputStream(file);
  }

  // -------------------------------------------------------------------------------------------------------------------
  // private instance methods
  // -------------------------------------------------------------------------------------------------------------------

  private File getFile(SortedSet<Dependency> dependencies) throws NoSuchAlgorithmException,
    UnsupportedEncodingException {
    StringBuilder builder = new StringBuilder();
    for(Dependency dependency : dependencies) {
      builder.append(dependency.getModuleName()).append(':');
    }
    builder.deleteCharAt(builder.length() - 1);
    MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
    messageDigest.update(builder.toString().getBytes("UTF-8"));
    return new File(outputDir, new String(Hex.encodeHex(messageDigest.digest())) + ".js");
  }

}
